//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.tuiofx.internal.gesture;

import java.util.Timer;
import java.util.TimerTask;

import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.input.ScrollEvent;
import org.tuiofx.Configuration;

public class ScrollGestureRecognizer extends GestureRecognizer {
    private final LongPressDetector longPressDetector;
    private float touchSlopSquare;
    private static float TOUCH_SLOP_DP = 0.01F;
    private DragData dragData;
    private boolean isScrolling = false;
    private float minimumInertiaVelocity = 0.4F;
    private final long FAKE_FRAME_TIME = 10L;
    private TimerTask timerTask;

    public ScrollGestureRecognizer(Node targetNode, Configuration config) {
        super(targetNode, config);
        this.requiredNumberOfTouches = 1;
        this.touchSlopSquare = TOUCH_SLOP_DP * TOUCH_SLOP_DP;
        this.longPressDetector = new LongPressDetector(config.getLongPressDuration());
    }

    public void touchesBegan(TuioTouchPoint touchPoint) {
        this.longPressDetector.touchesBegan(touchPoint);
        if (this.getTargetNode() != null && this.touchPointsCount >= this.requiredNumberOfTouches) {
            if (this.isScrolling) {
                this.isScrolling = false;
                if (this.timerTask != null) {
                    this.timerTask.cancel();
                }

                this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_FINISHED, 0.0, 0.0, 0.0, 0.0);
            }

        }
    }

    public void touchesMove(TuioTouchPoint touchPoint) {
        this.longPressDetector.touchesMove(touchPoint);
        if (this.getTargetNode() != null && this.touchPointsCount >= this.requiredNumberOfTouches) {
            if (this.touchWithinMaxPoints(touchPoint)) {
                Point2D centroid = this.getCentroid();
                double lastCentroidX;
                double lastCentroidY;
                if (this.dragData == null) {
                    lastCentroidX = centroid.getX();
                    lastCentroidY = centroid.getY();
                    this.dragData = new DragData(centroid.getX(), centroid.getY());
                } else {
                    lastCentroidX = this.dragData.getLastCentroidX();
                    lastCentroidY = this.dragData.getLastCentroidY();
                    this.dragData.setLastCentroidX(centroid.getX());
                    this.dragData.setLastCentroidY(centroid.getY());
                }

                double deltaX = centroid.getX() - lastCentroidX;
                double deltaY = centroid.getY() - lastCentroidY;
                double squareDistance = deltaX * deltaX + deltaY * deltaY;
                boolean recognizedScrolling = false;
                if (squareDistance > (double) this.touchSlopSquare) {
                    recognizedScrolling = true;
                }

                if (recognizedScrolling && !this.isScrolling) {
                    this.dragData.setInitX(this.getCentroid().getX());
                    this.dragData.setInitY(this.getCentroid().getY());
                    this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_STARTED, 0.0, 0.0, 0.0, 0.0);
                    this.isScrolling = true;
                } else if (recognizedScrolling && this.isScrolling) {
                    this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL, deltaX, deltaY, deltaX, deltaY);
                }

            }
        }
    }

    public void touchesEnd(final TuioTouchPoint touchPoint) {
        this.longPressDetector.touchesEnd(touchPoint);
        if (this.getTargetNode() != null && this.isScrolling) {
            double x = touchPoint.getSceneX();
            double y = touchPoint.getSceneY();
            double screenX = touchPoint.getScreenX();
            double screenY = touchPoint.getScreenY();
            if (this.touchPointsCount > 0) {
                Point2D centroid = this.getCentroid();
                this.dragData.setLastCentroidX(centroid.getX());
                this.dragData.setLastCentroidY(centroid.getY());
                this.dragData.setInitX(centroid.getX());
                this.dragData.setInitY(centroid.getY());
                this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_FINISHED, x, y, screenX, screenY, 0.0, 0.0, 0.0, 0.0, false);
                this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_STARTED, x, y, screenX, screenY, 0.0, 0.0, 0.0, 0.0, false);
            } else if (this.touchPointsCount == 0) {
                boolean inertia = false;
                float velocityX = touchPoint.getCursor().getXSpeed();
                float velocityY = touchPoint.getCursor().getYSpeed();
                if (Math.abs(velocityX) >= this.minimumInertiaVelocity || Math.abs(velocityY) >= this.minimumInertiaVelocity) {
                    inertia = true;
                    final int timeConstant = 325;
                    final double amplitudeX = 10.8 * (double) velocityX;
                    final double amplitudeY = 10.8 * (double) velocityY;
                    final double targetX = touchPoint.getSceneX() + amplitudeX;
                    final double targetY = touchPoint.getSceneY() + amplitudeY;
                    final double targetScreenX = touchPoint.getScreenX() + amplitudeX;
                    final double targetScreenY = touchPoint.getScreenY() + amplitudeY;
                    final long timestamp = System.nanoTime();
                    this.timerTask = new TimerTask() {
                        public void run() {
                            ScrollGestureRecognizer.this.inertia(amplitudeX, amplitudeY, targetX, targetY, targetScreenX, targetScreenY, timestamp, timeConstant, touchPoint);
                        }
                    };
                    (new Timer()).schedule(this.timerTask, 0L, FAKE_FRAME_TIME);
                }

                if (!inertia) {
                    this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_FINISHED, x, y, screenX, screenY, 0.0, 0.0, 0.0, 0.0, false);
                    this.isScrolling = false;
                }
            }

        }
    }

    private void inertia(double amplitudeX, double amplitudeY, double targetX, double targetY, double targetScreenX, double targetScreenY, long timestamp, int timeConstant, TuioTouchPoint touchPoint) {
        double elapsed = (double) (System.nanoTime() - timestamp) / 1000000.0;
        double inertiaDeltaX = amplitudeX * Math.exp(-elapsed / (double) timeConstant);
        double inertiaDeltaY = amplitudeY * Math.exp(-elapsed / (double) timeConstant);
        double x = 0.0;
        double y = 0.0;
        double screenX = 0.0;
        double screenY = 0.0;
        if (!(Math.abs(inertiaDeltaX) > 0.5) && !(Math.abs(inertiaDeltaY) > 0.5)) {
            x = targetX;
            y = targetY;
            screenX = targetScreenX;
            screenY = targetScreenY;
            this.timerTask.cancel();
        } else {
            x = targetX - inertiaDeltaX;
            y = targetY - inertiaDeltaY;
            screenX = targetScreenX - inertiaDeltaX;
            screenY = targetScreenY - inertiaDeltaY;
        }

        this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL, x, y, screenX, screenY, inertiaDeltaX, inertiaDeltaY, inertiaDeltaX, inertiaDeltaY, true);
        if (x == targetX && y == targetY) {
            this.fireScrollEvent(touchPoint, ScrollEvent.SCROLL_FINISHED, x, y, screenX, screenY, inertiaDeltaX, inertiaDeltaY, inertiaDeltaX, inertiaDeltaY, true);
            this.isScrolling = false;
        }

    }

    private static class DragData {
        private double lastCentroidX = 0.0;
        private double lastCentroidY = 0.0;
        private double initX = 0.0;
        private double initY = 0.0;

        DragData(double lastCentroidX, double lastCentroidY) {
            this.lastCentroidX = lastCentroidX;
            this.lastCentroidY = lastCentroidY;
        }

        public void setLastCentroidX(double lastX) {
            this.lastCentroidX = lastX;
        }

        public void setLastCentroidY(double lastY) {
            this.lastCentroidY = lastY;
        }

        public double getLastCentroidX() {
            return this.lastCentroidX;
        }

        public double getLastCentroidY() {
            return this.lastCentroidY;
        }

        public double getInitX() {
            return this.initX;
        }

        public void setInitX(double initX) {
            this.initX = initX;
        }

        public double getInitY() {
            return this.initY;
        }

        public void setInitY(double initY) {
            this.initY = initY;
        }
    }
}
